#include "stdafx.h"
#include "Renderer.h"


GLRenderer::GLRenderer()
{

}

GLRenderer::~GLRenderer()
{
	glfwDestroyWindow(pWindow);
	glfwTerminate();
	//exit(EXIT_SUCCESS);
}

void GLRenderer::initialize(GLFWwindow * window, RenderMode mode)
{
	pWindow = window;
	renderMode = mode;
	prepareValuesForMode();

	if (!glfwInit())	exit(EXIT_FAILURE);
	pWindow = glfwCreateWindow(screenWidth*2,screenHeight, "My Windows", NULL, NULL);
	if (!pWindow){
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
	glfwHideWindow(pWindow);
	glfwMakeContextCurrent(pWindow);
	//glfwSwapInterval(2);

	//OpenGL Settings
	glEnable( GL_DEPTH_TEST );
	initializePixelBufs();
	rHand.generateVertexBuffers();
}

void GLRenderer::prepareValuesForMode()
{
	data_width = SIZE_IMAGE_PARTICLE_WIDTH;
	data_height = SIZE_IMAGE_PARTICLE_HEIGHT*NUM_TOTAL_PARTICLES;
	particle_data_width = SIZE_IMAGE_PARTICLE_WIDTH;
	particle_data_height = SIZE_IMAGE_PARTICLE_HEIGHT;

	frustum_near = KINECT_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS;
	frustum_far = frustum_near+1000;
	switch (renderMode)
	{
	case DRAW_CROP_128:
		screenWidth = 128;
		screenHeight = 128;
		break;
	case DRAW_FULL_COLOR:
	default:
		screenWidth = K_WINDOWS_WIDTH;
		screenHeight = K_WINDOWS_HEIGHT;
		break;
	}

	frustumMat(-screenWidth/2,screenWidth/2,-screenHeight/2,screenHeight/2,frustum_near,frustum_far);
}

void GLRenderer::initializePixelBufs()
{
	glewInit();
	int DATA_SIZE = data_width*data_height*sizeof(unsigned short);
	glGenBuffersARB(1, &obsDepthBuffer);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, obsDepthBuffer);
	glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
	glGenBuffersARB(1, &obsSkinBuffer);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, obsSkinBuffer);
	glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
	glGenBuffersARB(1, &renBuffer);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, renBuffer);
	glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);

	glGenRenderbuffers(1, &renderFrameBuffer);
	glBindRenderbuffer(GL_RENDERBUFFER, renderFrameBuffer);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, data_width, data_height);
	glBindRenderbuffer(GL_RENDERBUFFER, 0);

	glGenFramebuffers( 1 , &frameBuffer );
	glBindFramebuffer( GL_FRAMEBUFFER , frameBuffer );
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderFrameBuffer);
	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if(status == GL_FRAMEBUFFER_COMPLETE) printf("setting ok \n");
	glBindFramebuffer( GL_FRAMEBUFFER , 0 );

	//** BONUS **//
	previewBuffer = new uchar[128*128*3];
	glGenRenderbuffers(1, &preview_RenderColorFB);
	glBindRenderbuffer(GL_RENDERBUFFER, preview_RenderColorFB);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 128, 128);
	glBindRenderbuffer(GL_RENDERBUFFER, 0);

	glGenRenderbuffers(1, &preview_RenderDepthFB);
	glBindRenderbuffer(GL_RENDERBUFFER, preview_RenderDepthFB);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 128, 128);
	glBindRenderbuffer(GL_RENDERBUFFER, 0);

	glGenFramebuffers( 1 , &preview_FB );
	glBindFramebuffer( GL_FRAMEBUFFER , preview_FB );
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, preview_RenderColorFB);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, preview_RenderDepthFB);
	glBindFramebuffer( GL_FRAMEBUFFER , 0 );
}

void GLRenderer::rendering()
{
	if (renderMode != DRAW_FULL_COLOR)	matrixFrustum_focus = matrixFrustum;
	
#ifdef PERFORMANCE_TEST
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	for (int i=0;i<64;i++)
	{
		renderHandModelGL();
	}
	glFinish();
#else
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	renderHandModelGL();
	glfwSwapBuffers(pWindow);
	glfwPollEvents();
#endif
}

void GLRenderer::renderHandModelGL()
{
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(matrixFrustum_focus.data());
	glMatrixMode(GL_MODELVIEW);glLoadIdentity();
	rHand.drawHand();
}

void GLRenderer::renderMultiModelGL(std::vector<Hypothesis> &hypos)
{
	glBindFramebuffer( GL_FRAMEBUFFER , frameBuffer );
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColorMask(0,0,0,0);     // Disable color, it's useless, we only want depth.
	glDepthMask(GL_TRUE);     // Ask z writing
	
	int stepW =	particle_data_width;
	int stepH = particle_data_height;
	for (int i=0;	i<hypos.size();	i++){
		/*SPLIT MODELS TO VIEWPORTs*/
		rHand.handDOFs = hypos[i].dofData;
		glViewport(0,i*stepH,stepW,stepH);
		renderHandModelGL();	
	}
	glBindFramebuffer( GL_FRAMEBUFFER , 0 );
}

void GLRenderer::renderHandModelGL_Comparing(Hypothesis observeModel,Hypothesis tempModel)
{
	glColorMask(1,1,1,1);     // Disable color, it's useless, we only want depth.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	glViewport(0,0,128,128);glLoadIdentity();
	rHand.handDOFs = observeModel.dofData;
	renderHandModelGL();

	glViewport(128,0,128,128);glLoadIdentity();
	rHand.handDOFs = tempModel.dofData;
	renderHandModelGL();

	glfwSwapBuffers(pWindow);
	glfwPollEvents();
}

void GLRenderer::renderHandModelGL_For_Preview(Hypothesis observeModel)
{
	glBindFramebuffer( GL_FRAMEBUFFER , preview_FB );
	glColorMask(1,1,1,1);     // Disable color, it's useless, we only want depth.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	glViewport(0,0,128,128);glLoadIdentity();
	rHand.handDOFs = observeModel.dofData;
	renderHandModelGL();

	glReadBuffer(GL_COLOR_ATTACHMENT0);
	glReadPixels(0, 0, 128, 128, GL_BGR, GL_UNSIGNED_BYTE, previewBuffer);
	glBindFramebuffer( GL_FRAMEBUFFER , 0 );
}

void GLRenderer::putDepthToBuf(GLuint & buffer)
{
	glBindFramebuffer( GL_FRAMEBUFFER , frameBuffer );
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, buffer);
	glReadBuffer(GL_DEPTH_ATTACHMENT); // Lay buffer phia truoc
	glReadPixels(0, 0, data_width, data_height, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,0); // Lay do sau
	glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
	glBindFramebuffer( GL_FRAMEBUFFER , 0 );
}

void GLRenderer::frustumMat(GLint left, GLint right, GLint bottom, GLint top, GLfloat zNear, GLfloat zFar)
{
	GLfloat zDelta = (zFar-zNear);
	GLfloat dir = GLfloat(right-left);
	GLfloat height = GLfloat(top-bottom);
	GLfloat zNear2 = (2*zNear);

	matrixFrustum(0,0) = 2.0f*zNear/dir;
	matrixFrustum(0,1) = 0.0f;
	matrixFrustum(0,2) = (right+left)/dir;
	matrixFrustum(0,3) = 0.0f;
	matrixFrustum(1,0) = 0.0f;
	matrixFrustum(1,1) = zNear2/height;
	matrixFrustum(1,2) = (top+bottom)/height;
	matrixFrustum(1,3) = 0.0f;
	matrixFrustum(2,0) = 0.0f;
	matrixFrustum(2,1) = 0.0f;
	matrixFrustum(2,2) = -(zFar+zNear)/zDelta;
	matrixFrustum(2,3) = -zNear2*zFar/zDelta;
	matrixFrustum(3,0) = 0.0f;
	matrixFrustum(3,1) = 0.0f;
	matrixFrustum(3,2) = -1.0f;
	matrixFrustum(3,3) = 0.0f;
}

/*ROTATING CAMERA VIEW ___ with ___ NOTE: z < 0 && x *= -1 */
void GLRenderer::calculatingHandCenter(cv::Vec3d handCenter)
{
	/* CHECK BUG */
	_ASSERT(handCenter[2] > KINECT_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS);

	using namespace Eigen;
	/*Update Frustum_focus*/
	AngleAxisf rotX(atanf(handCenter[1]/-handCenter[2]), Vector3f::UnitX());
	AngleAxisf rotY(atanf(-handCenter[0]/-handCenter[2]), Vector3f::UnitY());
	matrixFrustum_focus = matrixFrustum*Affine3f(rotY*rotX).matrix();
}